/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include <TextUtils.h>
#include <QTML.h>

#include "QuickTimeMediaFile.h"


using namespace Scenic::Renderer;


QuickTimeMediaFile::QuickTimeMediaFile(const char *filename, Image *image)
{
	Synchronized sync(this);
	::Rect movieBox;
	FSSpec fileSpec;
	OSErr result;
	short fileRef = 0;

	printf("DBG: QuickTimeMediaFile::QuickTimeMediaFile(\"%s\")\n", filename);

	this->filename = strdup(filename);
	this->image = image;

	InitializeQTML(0);
	result = EnterMovies();
	if(result != noErr)
	{
		fprintf(stderr, "Error: QT EnterMovies failed (result=%d)\n", result);
		return;
	}
	result = NativePathNameToFSSpec(this->filename, &fileSpec, kErrorIfFileNotFound);
	if(result != noErr)
	{
		fprintf(stderr, "Error: QT NativePathNameToFSSpec failed (result=%d)\n", result);
		return;
	}

	result = OpenMovieFile(&fileSpec, &fileRef, fsRdPerm);
	if(result != noErr)
	{
		fprintf(stderr, "Error: QT OpenMovieFile failed (result=%d)\n", result);
		return;
	}

	result = NewMovieFromFile(&movie, fileRef, nil, nil, newMovieActive, nil);
	CloseMovieFile(fileRef);
	if(result != noErr)
	{
		fprintf(stderr, "Error: QT NewMovieFromFile failed (result=%d)\n", result);
		return;
	}

	GetMovieBox(movie, &movieBox);

	movieBox.right -= movieBox.left;
	movieBox.bottom -= movieBox.top;
	movieBox.left = 0;
	movieBox.top = 0;

    SetMovieBox(movie, &movieBox);

	renderingLock.enterCriticalSection();
	image->create(movieBox.right, movieBox.bottom, FMT_R8G8B8);
	renderingLock.leaveCriticalSection();

	printf("width: %d, height: %d\n", image->width, image->height);
	createOffscreenGWorld(image->width, image->height);
	StartMovie(movie);
	createThread();
}

QuickTimeMediaFile::~QuickTimeMediaFile()
{
	if(thread)
	{
		exitThread = true;
		WaitForSingleObject(thread, INFINITE);
		thread = 0;
	}
	if(movie)
	{
		DisposeMovie(movie);
	}
	if(dc)
	{
		DeleteDC(dc);
	}
	if(bitmap)
	{
		DeleteObject(bitmap);
	}
}

void QuickTimeMediaFile::createOffscreenGWorld(int width, int height)
{
	HDC hdcTemp;
	BITMAPINFO *bitmapInfo = nil;
	OSErr result;
	
	hdcTemp = GetDC(NULL);
	dc = CreateCompatibleDC(hdcTemp);
	ReleaseDC(NULL,hdcTemp);	
	
	bitmapInfo = (BITMAPINFO*)calloc(1, sizeof(BITMAPINFOHEADER)
		+ (3 * sizeof(DWORD)));
	
	bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfo->bmiHeader.biWidth = width;
	bitmapInfo->bmiHeader.biHeight =  -1 * height;     // top down dib
	bitmapInfo->bmiHeader.biPlanes = 1;
	bitmapInfo->bmiHeader.biBitCount = 24;
	bitmapInfo->bmiHeader.biCompression = BI_RGB;
	bitmapInfo->bmiHeader.biSizeImage = 0;
	bitmapInfo->bmiHeader.biXPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biYPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biClrUsed = 0;
	bitmapInfo->bmiHeader.biClrImportant = 0;
	bitmapInfo->bmiColors[0].rgbBlue = 0;
	bitmapInfo->bmiColors[0].rgbGreen = 0;
	bitmapInfo->bmiColors[0].rgbRed = 0;
	bitmapInfo->bmiColors[0].rgbReserved = 0;
	
	bitmap = CreateDIBSection(dc, bitmapInfo, DIB_RGB_COLORS,
		&bitmapData, NULL, NULL);	
	
	free(bitmapInfo);

	// bail if DIB creation failed
	
	if (bitmap == 0) {
		fprintf(stderr, "Error: QT CreateDIBSection failed\n");
		return;
	}
	
	result = NewGWorldFromHBITMAP(&gWorld, NULL, NULL, NULL, bitmap, dc);
		
	if(result != noErr)
	{
		fprintf(stderr, "Error: QT NewGWorldFromHBITMAP failed (result=%d)\n", result);
		return;
	}
	
	SetMovieGWorld(movie, gWorld, NULL);
}

unsigned long __stdcall QuickTimeMediaFile::runWrapper(void *data)
{
	return ((QuickTimeMediaFile*)data)->run();
}

void QuickTimeMediaFile::createThread()
{
	exitThread = false;
	thread = CreateThread(NULL, 0, runWrapper, this, 0, &threadId);
}

short QuickTimeMediaFile::drawingCallbackWrapper(Movie movie, long data)
{
	((QuickTimeMediaFile*)(void*)data)->drawingCallback();
	return 1;
}

void QuickTimeMediaFile::drawingCallback()
{
	jobject obj = image->getJavaObject();
	int pitch = (image->width * 3 + 3) / 4 * 4;

	{
		leaveCriticalSection();
		renderingLock.enterCriticalSection();
		image->write(0, 0, image->width, image->height, (char*)bitmapData, pitch, image->format);
		renderingLock.leaveCriticalSection();
		enterCriticalSection();
	}
	if(obj)
	{
		jclass clazz = threadEnv->GetObjectClass(obj);
		jmethodID markChangedMethod = threadEnv->GetMethodID(clazz, "markChanged", "()V");
		
		threadEnv->CallVoidMethod(obj, markChangedMethod);
	}
}

unsigned long QuickTimeMediaFile::run()
{
	image->getJavaVM()->AttachCurrentThread((void**)&threadEnv, NULL);
	enterCriticalSection();
	SetMovieDrawingCompleteProc(movie, movieDrawingCallWhenChanged, drawingCallbackWrapper, (long)(void*)this);
	leaveCriticalSection();
	while(!exitThread)
	{
		enterCriticalSection();
		MoviesTask(movie, 0);
		leaveCriticalSection();
		Sleep(1);
	}
	image->getJavaVM()->DetachCurrentThread();

	return 0;
}

void QuickTimeMediaFile::play()
{
	Synchronized sync(this);

	StartMovie(movie);
}

void QuickTimeMediaFile::pause()
{
	Synchronized sync(this);

	StopMovie(movie);
}

double QuickTimeMediaFile::getCurrentPosition()
{
	Synchronized sync(this);

	return GetMovieTime(movie, NULL) / 1000.0;
}

void QuickTimeMediaFile::setCurrentPosition(double position)
{
	Synchronized sync(this);
}

double QuickTimeMediaFile::getLength()
{
	Synchronized sync(this);

	return GetMovieDuration(movie) / 1000.0;
}

void QuickTimeMediaFile::enumerateOutputFormats() 
{
}

void QuickTimeMediaFile::setOutputFormat(int width, int height, Format format)
{
}
